#include "usbd_core.h"
#include "usbd_cdc.h"
#include "usb_service.h"
#include <rtthread.h>
#include <rtdevice.h>
#include "DAP.h"

struct usbd_interface intf0;
struct usbd_interface intf1;

static uart_config_t uart_config = {0};
static usb2uart_cb usb2uart_func = NULL; 
static uart_config_cb uart_config_func = NULL;
static uint8_t usb2uart_buff[USB2UART_PACK_SIZE] = {0};

static rt_uint16_t usb2dap_index = 0;
static usb2dap_cb usb2dap_func = NULL;
static rt_uint8_t usb2dap_buff[USB2DAP_PACK_COUNT][USB2DAP_PACK_SIZE] = {0};

const uint8_t cdc_winusb_descriptor[] = {
    USB_DEVICE_DESCRIPTOR_INIT(USB_2_1, 0xEF, 0x02, 0x01, USBD_VID, USBD_PID, 0x0100, 0x01),
    USB_CONFIG_DESCRIPTOR_INIT(USB_CONFIG_SIZE, 0x03, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
    USB_INTERFACE_DESCRIPTOR_INIT(0x00, 0x00, 0x02, 0xff, 0x00, 0x00, 0x02),
    USB_ENDPOINT_DESCRIPTOR_INIT(WINUSB_OUT_EP, 0x02, USB_MAX_MPS, 0x00),
    USB_ENDPOINT_DESCRIPTOR_INIT(WINUSB_IN_EP, 0x02, USB_MAX_MPS, 0x00),
    CDC_ACM_DESCRIPTOR_INIT(0x01, CDC_INT_EP, CDC_OUT_EP, CDC_IN_EP, USB_MAX_MPS, 0x00),
    ///////////////////////////////////////
    /// string0 descriptor
    ///////////////////////////////////////
    USB_LANGID_INIT(USBD_LANGID_STRING),
    ///////////////////////////////////////
    /// string1 descriptor
    ///////////////////////////////////////
    0x12,                       /* bLength */
    USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
    'R', 0x00,                  /* wcChar0 */
    'i', 0x00,                  /* wcChar1 */
    'c', 0x00,                  /* wcChar2 */
    'e', 0x00,                  /* wcChar3 */
    'C', 0x00,                  /* wcChar4 */
    'h', 0x00,                  /* wcChar5 */
    'e', 0x00,                  /* wcChar6 */
    'n', 0x00,                  /* wcChar7 */
    ///////////////////////////////////////
    /// string2 descriptor
    ///////////////////////////////////////
    0x1E,                       /* bLength */
    USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
    'R', 0x00,                  /* wcChar0 */
    'i', 0x00,                  /* wcChar1 */
    'c', 0x00,                  /* wcChar2 */
    'e', 0x00,                  /* wcChar3 */
    ' ', 0x00,                  /* wcChar4 */
    'C', 0x00,                  /* wcChar5 */
    'M', 0x00,                  /* wcChar6 */
    'S', 0x00,                  /* wcChar7 */
    'I', 0x00,                  /* wcChar8 */
    'S', 0x00,                  /* wcChar9 */
    '-', 0x00,                  /* wcChar10 */
    'D', 0x00,                  /* wcChar11 */
    'A', 0x00,                  /* wcChar12 */
    'P', 0x00,                  /* wcChar13 */
    ///////////////////////////////////////
    /// string3 descriptor
    ///////////////////////////////////////
    0x1C,                       /* bLength */
    USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
    'R', 0x00,                  /* wcChar0 */
    'i', 0x00,                  /* wcChar1 */
    'c', 0x00,                  /* wcChar2 */
    'e', 0x00,                  /* wcChar3 */
    '-', 0x00,                  /* wcChar4 */
    '2', 0x00,                  /* wcChar5 */
    '0', 0x00,                  /* wcChar6 */
    '2', 0x00,                  /* wcChar7 */
    '3', 0x00,                  /* wcChar8 */
    '0', 0x00,                  /* wcChar9 */
    '1', 0x00,                  /* wcChar10 */
    '0', 0x00,                  /* wcChar11 */
    '1', 0x00,                  /* wcChar12 */
#ifdef CONFIG_USB_HS
    ///////////////////////////////////////
    /// device qualifier descriptor
    ///////////////////////////////////////
    0x0a,
    USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,
    0x00,
    0x02,
    0x02,
    0x02,
    0x01,
    0x40,
    0x01,
    0x00,
#endif
    0x00
};

__ALIGN_BEGIN const uint8_t USBD_WinUSBDescriptorSetDescriptor[] = {
    WBVAL(WINUSB_DESCRIPTOR_SET_HEADER_SIZE), /* wLength */
    WBVAL(WINUSB_SET_HEADER_DESCRIPTOR_TYPE), /* wDescriptorType */
    0x00, 0x00, 0x03, 0x06, /* >= Win 8.1 */  /* dwWindowsVersion*/
    WBVAL(USBD_WINUSB_DESC_SET_LEN),          /* wDescriptorSetTotalLength */
    WBVAL(WINUSB_FUNCTION_SUBSET_HEADER_SIZE), /* wLength */
    WBVAL(WINUSB_SUBSET_HEADER_FUNCTION_TYPE), /* wDescriptorType */
    0,                                         /* bFirstInterface USBD_BULK_IF_NUM*/
    0,                                         /* bReserved */
    WBVAL(FUNCTION_SUBSET_LEN),                /* wSubsetLength */
    WBVAL(WINUSB_FEATURE_COMPATIBLE_ID_SIZE),  /* wLength */
    WBVAL(WINUSB_FEATURE_COMPATIBLE_ID_TYPE),  /* wDescriptorType */
    'W', 'I', 'N', 'U', 'S', 'B', 0, 0,        /* CompatibleId*/
    0, 0, 0, 0, 0, 0, 0, 0,                    /* SubCompatibleId*/
    WBVAL(DEVICE_INTERFACE_GUIDS_FEATURE_LEN), /* wLength */
    WBVAL(WINUSB_FEATURE_REG_PROPERTY_TYPE),   /* wDescriptorType */
    WBVAL(WINUSB_PROP_DATA_TYPE_REG_MULTI_SZ), /* wPropertyDataType */
    WBVAL(42),                                 /* wPropertyNameLength */
    'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0,
    'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, 'e', 0,
    'G', 0, 'U', 0, 'I', 0, 'D', 0, 's', 0, 0, 0,
    WBVAL(80), /* wPropertyDataLength */
    '{', 0,
    'C', 0, 'D', 0, 'B', 0, '3', 0, 'B', 0, '5', 0, 'A', 0, 'D', 0, '-', 0,
    '2', 0, '9', 0, '3', 0, 'B', 0, '-', 0,
    '4', 0, '6', 0, '6', 0, '3', 0, '-', 0,
    'A', 0, 'A', 0, '3', 0, '6', 0, '-',
    0, '1', 0, 'A', 0, 'A', 0, 'E', 0, '4', 0, '6', 0, '4', 0, '6', 0, '3', 0, '7', 0, '7', 0, '6', 0,
    '}', 0, 0, 0, 0, 0
};

__ALIGN_BEGIN const uint8_t USBD_BinaryObjectStoreDescriptor[] = {
    0x05,                         /* bLength */
    0x0f,                         /* bDescriptorType */
    WBVAL(USBD_BOS_WTOTALLENGTH), /* wTotalLength */
    USBD_NUM_DEV_CAPABILITIES,    /* bNumDeviceCaps */
    USBD_WINUSB_DESC_LEN,           /* bLength */
    0x10,                           /* bDescriptorType */
    USB_DEVICE_CAPABILITY_PLATFORM, /* bDevCapabilityType */
    0x00,                           /* bReserved */
    0xDF, 0x60, 0xDD, 0xD8,         /* PlatformCapabilityUUID */
    0x89, 0x45, 0xC7, 0x4C,
    0x9C, 0xD2, 0x65, 0x9D,
    0x9E, 0x64, 0x8A, 0x9F,
    0x00, 0x00, 0x03, 0x06, /* >= Win 8.1 */ /* dwWindowsVersion*/
    WBVAL(USBD_WINUSB_DESC_SET_LEN),         /* wDescriptorSetTotalLength */
    USBD_WINUSB_VENDOR_CODE,                 /* bVendorCode */
    0,                                       /* bAltEnumCode */
};

struct usb_msosv2_descriptor msosv2_desc = {
    .vendor_code = USBD_WINUSB_VENDOR_CODE,
    .compat_id = (uint8_t *)USBD_WinUSBDescriptorSetDescriptor,
    .compat_id_len = USBD_WINUSB_DESC_SET_LEN,
};

struct usb_bos_descriptor bos_desc = {
    .string = (uint8_t *)USBD_BinaryObjectStoreDescriptor,
    .string_len = USBD_BOS_WTOTALLENGTH
};

void usbd_event_handler(uint8_t event)
{
    switch (event) {
        case USBD_EVENT_RESET:
            break;
        case USBD_EVENT_CONNECTED:
            break;
        case USBD_EVENT_DISCONNECTED:
            break;
        case USBD_EVENT_RESUME:
            break;
        case USBD_EVENT_SUSPEND:
            break;
        case USBD_EVENT_CONFIGURED:
            /* setup first out ep read transfer */
            usbd_ep_start_read(CDC_OUT_EP, usb2uart_buff, USB2UART_PACK_SIZE);
            usb2dap_index = 0;
            usbd_ep_start_read(WINUSB_OUT_EP, usb2dap_buff[usb2dap_index], USB2DAP_PACK_SIZE);
            break;
        case USBD_EVENT_SET_REMOTE_WAKEUP:
            break;
        case USBD_EVENT_CLR_REMOTE_WAKEUP:
            break;

        default:
            break;
    }
}

/* USB-DAP */
void usb_service_dap2usb(uint8_t *data, uint16_t len)
{
    usbd_ep_start_write(WINUSB_IN_EP, data, len);
}

void usb_service_usb2dap_reg(usb2uart_cb cb)
{
    usb2dap_func = cb;
}

void usbd_winusb_out(uint8_t ep, uint32_t nbytes)
{
    if(usb2dap_func)
    {
        usb2dap_func(usb2dap_buff[usb2dap_index], nbytes);
        usb2dap_index++;

        if(usb2dap_index >= USB2DAP_PACK_COUNT)
        {
            usb2dap_index = 0;
        }
    }   
    usbd_ep_start_read(WINUSB_OUT_EP, usb2dap_buff[usb2dap_index], USB2DAP_PACK_SIZE);
}

void usbd_winusb_in(uint8_t ep, uint32_t nbytes)
{
    if ((nbytes % USB_MAX_MPS) == 0 && nbytes) {
        usbd_ep_start_write(WINUSB_IN_EP, NULL, 0);
    }
}

struct usbd_endpoint winusb_out_ep = {
    .ep_addr = WINUSB_OUT_EP,
    .ep_cb = usbd_winusb_out
};

struct usbd_endpoint winusb_in_ep = {
    .ep_addr = WINUSB_IN_EP,
    .ep_cb = usbd_winusb_in
};

/* USB-UART */
void usb_service_uart2usb(uint8_t *data, uint16_t len)
{
    usbd_ep_start_write(CDC_IN_EP, (const uint8_t *)data, len);
}

void usb_service_usb2uart_reg(usb2uart_cb cb)
{
    usb2uart_func = cb;
}

void usb_service_uart_config_reg(uart_config_cb cb)
{
    uart_config_func = cb;
}

void usbd_cdc_acm_bulk_out(uint8_t ep, uint32_t nbytes)
{
    if(usb2uart_func)
    {
        usb2uart_func(usb2uart_buff, nbytes);
    }
    usbd_ep_start_read(CDC_OUT_EP, usb2uart_buff, USB2UART_PACK_SIZE);
}

void usbd_cdc_acm_bulk_in(uint8_t ep, uint32_t nbytes)
{
    if ((nbytes % USB_MAX_MPS) == 0 && nbytes) {
        usbd_ep_start_write(CDC_IN_EP, NULL, 0);
    }
}

struct usbd_endpoint cdc_out_ep = {
    .ep_addr = CDC_OUT_EP,
    .ep_cb = usbd_cdc_acm_bulk_out
};

struct usbd_endpoint cdc_in_ep = {
    .ep_addr = CDC_IN_EP,
    .ep_cb = usbd_cdc_acm_bulk_in
};

void usbd_cdc_acm_set_line_coding(uint8_t intf, struct cdc_line_coding *line_coding)
{
    if(uart_config_func)
    {
        uart_config.baudrate = line_coding->dwDTERate;
        uart_config.databit = line_coding->bDataBits;
        uart_config.stopbit = line_coding->bCharFormat;
        uart_config.parity = line_coding->bParityType;

        uart_config_func(&uart_config);
    }
}

void usbd_cdc_acm_get_line_coding(uint8_t intf, struct cdc_line_coding *line_coding)
{
    memcpy(line_coding, (uint8_t *)&uart_config, sizeof(struct cdc_line_coding));
}

int usb_service_init(void)
{
    usbd_desc_register(cdc_winusb_descriptor);
    usbd_bos_desc_register(&bos_desc);
    usbd_msosv2_desc_register(&msosv2_desc);

    usbd_add_interface(&intf0);
    usbd_add_endpoint(&winusb_out_ep);
    usbd_add_endpoint(&winusb_in_ep);

    usbd_add_interface(usbd_cdc_acm_init_intf(&intf0));
    usbd_add_interface(usbd_cdc_acm_init_intf(&intf1));
    usbd_add_endpoint(&cdc_out_ep);
    usbd_add_endpoint(&cdc_in_ep);

    usbd_initialize();
    return RT_EOK;
}
INIT_COMPONENT_EXPORT(usb_service_init);
